#include <QXmlStreamWriter>
#include <QXmlAttributes>
#include "DataModel.h"
#include "XmlHelper.h"
#include "scImpl.h"

#define FCT "Request::Request(IKPK)"
Request::Request(PkType entity, PkType id, bool followFk, ScopeType scope) :
	rtype(REQU_READ), stype(scope), entity(entity), followFk(followFk), deadline(0)
{
	if(stype!=SCOPE_IK && stype!=SCOPE_PK)
		throw new AppException(FCT ": invalid scope", true);
	pks.push_back(id);
}

#undef FCT
#define FCT "Request::Request(XML)"
Request::Request(PkType entity, const QXmlAttributes & attributes) :
	rtype(REQU_READ), stype(SCOPE_UNDEF), entity(entity), followFk(false), deadline(0)
{
	stype=XmlHelper::scopeTypeText2scopeType(attributes.value(QString("type")));
	QString ffk(attributes.value(QString("follow_FK")));
	if(ffk.toLower()=="true" || ffk=="1")
		followFk=true;
	QDateTime qdt(QDateTime::fromString(attributes.value(QString("deadline_UTC"))));
	if(qdt.isValid())
	{
		QDateTime qdt2(qdt.date(), qdt.time(), Qt::UTC); // how can I specify the TimeSpec when converting rom QString???
		deadline=qdt2.toTime_t();
	}
}

#undef FCT
#define FCT "Request::Request(wr)"
Request::Request(PkType entity, PkType pk) :
	rtype(REQU_WRITE), stype(SCOPE_PK), entity(entity), followFk(false), deadline(0)
{
	pks.push_back(pk);
}

#undef FCT
#define FCT "Request::operator=="
bool Request::operator==(const Request & that) const
{
	bool res(rtype==that.rtype && stype==that.stype &&
			entity==that.entity && pks==that.pks && followFk==that.followFk && deadline==that.deadline);
	// ctrc(Trc::LOOP,APP,FCT)<<(*this)<<"=="<<that<<": "<<res<<endl;
	return res;
}

#undef FCT
#define FCT "Request::addKey"
void Request::addKey(PkType key)
{
	if(stype!=SCOPE_IK && stype!=SCOPE_PK)
		throw new AppException(FCT ": invalid scope", true);
	pks.push_back(key);
}

#undef FCT
#define FCT "Request::toXml"
void Request::toXml(QXmlStreamWriter * writer) const
{
	toXmlEntityList(writer, true);
	if(rtype==REQU_READ)
		toXmlScope(writer, true);
	if(rtype==REQU_WRITE)
		toXmlData(writer);
	toXmlEntityList(writer, false);
}

#undef FCT
#define FCT "Request::toXmlEntityList"
void Request::toXmlEntityList(QXmlStreamWriter * writer, bool begin) const
{
	if(begin)
	{
		writer->writeStartElement(QString("entity_list"));
		writer->writeAttribute(QString("type"), XmlHelper::requestType2requestTypeText(rtype));
		writer->writeAttribute(QString("tech_name"), DataModel::get()->entityId2TextQ(entity));
	}
	else
		writer->writeEndElement(); // entity
}

#undef FCT
#define FCT "Request::toXmlScope"
void Request::toXmlScope(QXmlStreamWriter * writer, bool writeKeys) const
{
	if(stype==SCOPE_UNDEF)
	{
		writer->writeEmptyElement(QString("scope"));
		return;
	}
	writer->writeStartElement(QString("scope"));
	if(followFk)
		writer->writeAttribute(QString("follow_FK"), QString("true"));
	bool addPkList(false);
	writer->writeAttribute(QString("type"), XmlHelper::scopeType2scopeTypeText(stype));
	switch(stype)
	{
	case SCOPE_TAKEALL:
		break;
	case SCOPE_TS:
		writer->writeAttribute(QString("deadline_UTC"), QDateTime::fromTime_t(deadline).toUTC().toString());
		break;
	case SCOPE_IK:
		addPkList=true;
		break;
	case SCOPE_IKTS:
		writer->writeAttribute(QString("deadline_UTC"), QDateTime::fromTime_t(deadline).toUTC().toString());
		addPkList=true;
		break;
	case SCOPE_PK:
		addPkList=true;
		break;
	default:
		throw new AppException(FCT ": unknown scope type", true);
	}
	if(writeKeys && addPkList)
	{
		for(Pks::const_iterator it(pks.begin()); it!=pks.end(); ++it)
		{
			writer->writeEmptyElement(QString("key"));
			writer->writeAttribute(QString("id"), QString::number(*it));
		}
	}
	writer->writeEndElement(); // scope
}

#undef FCT
#define FCT "Request::toXmlData"
void Request::toXmlData(QXmlStreamWriter * writer) const
{
	const DataTable_ & dt_(DataModel::get()->getTable(entity).getPayload());
	for(Pks::const_iterator it(pks.begin()); it!=pks.end(); ++it)
	{
		dt_.toXml(writer, *it, DUR_CLI);
	}
}

#undef FCT
#define FCT "Request::toString"
std::string Request::toString() const
{
	ostringstream os;
	os <<rtype<<"/"<<stype<<"ent"<<entity<<"#"<<pks.size()<<"f"<<followFk<<"dl"<<deadline;
	return os.str();
}

#undef FCT
#define FCT "std::operator<<(Request)"
ostream& std::operator<< (ostream& out, const sybilpm::Request & requ)
{
	out <<requ.toString().c_str();
	return out;
}
